home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / Make / source / w32 / subproc / sub_proc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-27  |  26.3 KB  |  1,132 lines

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <process.h>  /* for msvc _beginthreadex, _endthreadex */
  4. #include <windows.h>
  5.  
  6. #include "sub_proc.h"
  7. #include "proc.h"
  8. #include "w32err.h"
  9.  
  10. static char *make_command_line( char *shell_name, char *exec_path, char **argv);
  11.  
  12. typedef struct sub_process_t {
  13.     int sv_stdin[2];
  14.     int sv_stdout[2];
  15.     int sv_stderr[2];
  16.     int using_pipes;
  17.     char *inp;
  18.     DWORD incnt;
  19.     char * volatile outp;
  20.     volatile DWORD outcnt;
  21.     char * volatile errp;
  22.     volatile DWORD errcnt;
  23.     int pid;
  24.     int exit_code;
  25.     int signal;
  26.     long last_err;
  27.     long lerrno;
  28. } sub_process;
  29.  
  30. /* keep track of children so we can implement a waitpid-like routine */
  31. static sub_process *proc_array[256];
  32. static int proc_index = 0;
  33. static int fake_exits_pending = 0;
  34.  
  35. /*
  36.  * When a process has been waited for, adjust the wait state
  37.  * array so that we don't wait for it again
  38.  */
  39. static void
  40. process_adjust_wait_state(sub_process* pproc)
  41. {
  42.     int i;
  43.  
  44.     if (!proc_index)
  45.         return;
  46.  
  47.     for (i = 0; i < proc_index; i++)
  48.         if (proc_array[i]->pid == pproc->pid)
  49.             break;
  50.  
  51.     if (i < proc_index) {
  52.         proc_index--;
  53.         if (i != proc_index)
  54.             memmove(&proc_array[i], &proc_array[i+1], 
  55.                 (proc_index-i) * sizeof(sub_process*));
  56.         proc_array[proc_index] = NULL;
  57.     }
  58. }
  59.  
  60. /*
  61.  * Waits for any of the registered child processes to finish.
  62.  */
  63. static sub_process *
  64. process_wait_for_any_private(void)
  65. {
  66.     HANDLE handles[256];
  67.     DWORD retval, which;
  68.     int i;
  69.  
  70.     if (!proc_index)
  71.         return NULL;
  72.  
  73.     /* build array of handles to wait for */
  74.     for (i = 0; i < proc_index; i++) {
  75.         handles[i] = (HANDLE) proc_array[i]->pid;
  76.  
  77.         if (fake_exits_pending && proc_array[i]->exit_code)
  78.             break;
  79.     }
  80.  
  81.     /* wait for someone to exit */
  82.     if (!fake_exits_pending) {
  83.         retval = WaitForMultipleObjects(proc_index, handles, FALSE, INFINITE);
  84.         which = retval - WAIT_OBJECT_0;
  85.     } else {
  86.         fake_exits_pending--;
  87.         retval = !WAIT_FAILED;
  88.         which = i;
  89.     }
  90.  
  91.     /* return pointer to process */
  92.     if (retval != WAIT_FAILED) {
  93.         sub_process* pproc = proc_array[which];    
  94.         process_adjust_wait_state(pproc);
  95.         return pproc;
  96.     } else
  97.         return NULL;
  98. }
  99.  
  100. /*
  101.  * Terminate a process.
  102.  */
  103. BOOL
  104. process_kill(HANDLE proc, int signal)
  105. {
  106.     sub_process* pproc = (sub_process*) proc;
  107.     pproc->signal = signal;
  108.     return (TerminateProcess((HANDLE) pproc->pid, signal));
  109. }
  110.  
  111. /*
  112.  * Use this function to register processes you wish to wait for by
  113.  * calling process_file_io(NULL) or process_wait_any(). This must be done 
  114.  * because it is possible for callers of this library to reuse the same 
  115.  * handle for multiple processes launches :-(
  116.  */
  117. void
  118. process_register(HANDLE proc)
  119. {
  120.     proc_array[proc_index++] = (sub_process *) proc;
  121. }
  122.  
  123. /*
  124.  * Public function which works kind of like waitpid(). Wait for any
  125.  * of the children to die and return results. To call this function,
  126.  * you must do 1 of things:
  127.  *
  128.  *     x = process_easy(...);
  129.  *    
  130.  * or
  131.  *
  132.  *    x = process_init_fd();
  133.  *    process_register(x);
  134.  *
  135.  * or
  136.  *
  137.  *    x = process_init();
  138.  *    process_register(x);
  139.  *
  140.  * You must NOT then call process_pipe_io() because this function is
  141.  * not capable of handling automatic notification of any child
  142.  * death.
  143.  */
  144.  
  145. HANDLE
  146. process_wait_for_any(void)
  147. {
  148.     sub_process* pproc = process_wait_for_any_private(); 
  149.  
  150.     if (!pproc)
  151.         return NULL;
  152.     else {
  153.         /* 
  154.          * Ouch! can't tell caller if this fails directly. Caller 
  155.          * will have to use process_last_err() 
  156.                  */
  157.         (void) process_file_io(pproc);
  158.         return ((HANDLE) pproc);
  159.     }
  160. }
  161.  
  162. long
  163. process_errno(HANDLE proc)
  164. {
  165.     return (((sub_process *)proc)->lerrno);
  166. }
  167.  
  168. long
  169. process_signal(HANDLE proc)
  170. {
  171.     return (((sub_process *)proc)->signal);
  172. }
  173.  
  174.     long
  175. process_last_err(HANDLE proc)
  176. {
  177.     return (((sub_process *)proc)->last_err);
  178. }
  179.  
  180.     long
  181. process_exit_code(HANDLE proc)
  182. {
  183.     return (((sub_process *)proc)->exit_code);
  184. }
  185.  
  186.     char *
  187. process_outbuf(HANDLE proc)
  188. {
  189.     return (((sub_process *)proc)->outp);
  190. }
  191.  
  192.     char *
  193. process_errbuf(HANDLE proc)
  194. {
  195.     return (((sub_process *)proc)->errp);
  196. }
  197.  
  198.     int
  199. process_outcnt(HANDLE proc)
  200. {
  201.     return (((sub_process *)proc)->outcnt);
  202. }
  203.  
  204.     int
  205. process_errcnt(HANDLE proc)
  206. {
  207.     return (((sub_process *)proc)->errcnt);
  208. }
  209.  
  210.     void
  211. process_pipes(HANDLE proc, int pipes[3])
  212. {
  213.     pipes[0] = ((sub_process *)proc)->sv_stdin[0];
  214.     pipes[1] = ((sub_process *)proc)->sv_stdout[0];
  215.     pipes[2] = ((sub_process *)proc)->sv_stderr[0];
  216.     return;
  217. }
  218.  
  219.  
  220.     HANDLE
  221. process_init()
  222. {
  223.     sub_process *pproc;
  224.     /*
  225.      * open file descriptors for attaching stdin/stdout/sterr
  226.      */
  227.     HANDLE stdin_pipes[2];
  228.     HANDLE stdout_pipes[2];
  229.     HANDLE stderr_pipes[2];
  230.     SECURITY_ATTRIBUTES inherit;
  231.     BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
  232.  
  233.     pproc = malloc(sizeof(*pproc));
  234.     memset(pproc, 0, sizeof(*pproc));
  235.  
  236.     /* We can't use NULL for lpSecurityDescriptor because that
  237.        uses the default security descriptor of the calling process.
  238.        Instead we use a security descriptor with no DACL.  This
  239.        allows nonrestricted access to the associated objects. */
  240.     
  241.     if (!InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR)(&sd),
  242.                       SECURITY_DESCRIPTOR_REVISION)) {
  243.         pproc->last_err = GetLastError();
  244.         pproc->lerrno = E_SCALL;
  245.         return((HANDLE)pproc);
  246.     }
  247.  
  248.     inherit.nLength = sizeof(inherit);
  249.     inherit.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)(&sd);
  250.     inherit.bInheritHandle = TRUE;
  251.  
  252.     // By convention, parent gets pipe[0], and child gets pipe[1]
  253.     // This means the READ side of stdin pipe goes into pipe[1]
  254.     // and the WRITE side of the stdout and stderr pipes go into pipe[1]
  255.     if (CreatePipe( &stdin_pipes[1], &stdin_pipes[0], &inherit, 0) == FALSE ||
  256.     CreatePipe( &stdout_pipes[0], &stdout_pipes[1], &inherit, 0) == FALSE ||
  257.     CreatePipe( &stderr_pipes[0], &stderr_pipes[1], &inherit, 0) == FALSE) {
  258.  
  259.         pproc->last_err = GetLastError();
  260.         pproc->lerrno = E_SCALL;
  261.         return((HANDLE)pproc);
  262.     }
  263.  
  264.     //
  265.     // Mark the parent sides of the pipes as non-inheritable
  266.     //
  267.     if (SetHandleInformation(stdin_pipes[0], 
  268.                 HANDLE_FLAG_INHERIT, 0) == FALSE ||
  269.         SetHandleInformation(stdout_pipes[0], 
  270.                 HANDLE_FLAG_INHERIT, 0) == FALSE ||
  271.         SetHandleInformation(stderr_pipes[0], 
  272.                 HANDLE_FLAG_INHERIT, 0) == FALSE) {
  273.  
  274.         pproc->last_err = GetLastError();
  275.         pproc->lerrno = E_SCALL;
  276.         return((HANDLE)pproc);
  277.     }
  278.     pproc->sv_stdin[0]  = (int) stdin_pipes[0];
  279.     pproc->sv_stdin[1]  = (int) stdin_pipes[1];
  280.     pproc->sv_stdout[0] = (int) stdout_pipes[0];
  281.     pproc->sv_stdout[1] = (int) stdout_pipes[1];
  282.     pproc->sv_stderr[0] = (int) stderr_pipes[0];
  283.     pproc->sv_stderr[1] = (int) stderr_pipes[1];
  284.  
  285.     pproc->using_pipes = 1;
  286.  
  287.     pproc->lerrno = 0;
  288.  
  289.     return((HANDLE)pproc);
  290. }
  291.  
  292.  
  293.     HANDLE
  294. process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh)
  295. {
  296.     sub_process *pproc;
  297.  
  298.     pproc = malloc(sizeof(*pproc));
  299.     memset(pproc, 0, sizeof(*pproc));
  300.  
  301.     /*
  302.      * Just pass the provided file handles to the 'child side' of the
  303.      * pipe, bypassing pipes altogether.
  304.      */
  305.     pproc->sv_stdin[1]  = (int) stdinh;
  306.     pproc->sv_stdout[1] = (int) stdouth;
  307.     pproc->sv_stderr[1] = (int) stderrh;
  308.  
  309.     pproc->last_err = pproc->lerrno = 0;
  310.  
  311.     return((HANDLE)pproc);
  312. }
  313.  
  314.  
  315. static HANDLE
  316. find_file(char *exec_path, LPOFSTRUCT file_info)
  317. {
  318.     HANDLE exec_handle;
  319.     char *fname;
  320.     char *ext;
  321.  
  322.     fname = malloc(strlen(exec_path) + 5);
  323.     strcpy(fname, exec_path);
  324.     ext = fname + strlen(fname);
  325.  
  326.     strcpy(ext, ".exe");
  327.     if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
  328.             OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
  329.         free(fname);
  330.         return(exec_handle);
  331.     }
  332.  
  333.     strcpy(ext, ".cmd");
  334.     if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
  335.             OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
  336.         free(fname);
  337.         return(exec_handle);
  338.     }
  339.  
  340.     strcpy(ext, ".bat");
  341.     if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
  342.             OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
  343.         free(fname);
  344.         return(exec_handle);
  345.     }
  346.  
  347.     /* should .com come before this case? */
  348.     if ((exec_handle = (HANDLE)OpenFile(exec_path, file_info,
  349.             OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
  350.         free(fname);
  351.         return(exec_handle);
  352.     }
  353.  
  354.     strcpy(ext, ".com");
  355.     if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
  356.             OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
  357.         free(fname);
  358.         return(exec_handle);
  359.     }
  360.  
  361.     free(fname);
  362.     return(exec_handle);
  363. }
  364.  
  365.  
  366. /*
  367.  * Description:   Create the child process to be helped
  368.  *
  369.  * Returns: 
  370.  *
  371.  * Notes/Dependencies:  
  372.  */
  373. long
  374. process_begin(
  375.     HANDLE proc,
  376.     char **argv,
  377.     char **envp,
  378.     char *exec_path,
  379.     char *as_user)
  380. {
  381.     sub_process *pproc = (sub_process *)proc;
  382.     char *shell_name = 0;
  383.     int file_not_found=0;
  384.     HANDLE exec_handle;
  385.     char buf[256];
  386.     DWORD bytes_returned;
  387.     DWORD flags;
  388.     char *command_line;
  389.     STARTUPINFO startInfo;
  390.     PROCESS_INFORMATION procInfo;
  391.     char *envblk=NULL;
  392.     OFSTRUCT file_info;
  393.  
  394.  
  395.     /*
  396.      *  Shell script detection...  if the exec_path starts with #! then
  397.      *  we want to exec shell-script-name exec-path, not just exec-path
  398.      *  NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl.  We do not
  399.      *  hard-code the path to the shell or perl or whatever:  Instead, we
  400.      *  assume it's in the path somewhere (generally, the NT tools
  401.      *  bin directory)
  402.      *  We use OpenFile here because it is capable of searching the Path.
  403.      */
  404.  
  405.     exec_handle = find_file(exec_path, &file_info);
  406.  
  407.     /*
  408.      * If we couldn't open the file, just assume that Windows32 will be able
  409.      * to find and execute it.
  410.      */
  411.     if (exec_handle == (HANDLE)HFILE_ERROR) {
  412.         file_not_found++;
  413.     }
  414.     else {
  415.         /* Attempt to read the first line of the file */
  416.         if (ReadFile( exec_handle, 
  417.                 buf, sizeof(buf) - 1, /* leave room for trailing NULL */
  418.                 &bytes_returned, 0) == FALSE || bytes_returned < 2) {
  419.     
  420.             pproc->last_err = GetLastError();
  421.             pproc->lerrno = E_IO;
  422.             CloseHandle(exec_handle);
  423.             return(-1);
  424.         }
  425.         if (buf[0] == '#' && buf[1] == '!') {
  426.             /*
  427.              *  This is a shell script...  Change the command line from
  428.              *    exec_path args to shell_name exec_path args
  429.              */
  430.             char *p;
  431.     
  432.             /*  Make sure buf is NULL terminated */
  433.             buf[bytes_returned] = 0;
  434.             /*
  435.              * Depending on the file system type, etc. the first line
  436.              * of the shell script may end with newline or newline-carriage-return
  437.              * Whatever it ends with, cut it off.
  438.              */
  439.             p= strchr(buf, '\n');
  440.             if (p)
  441.                 *p = 0;
  442.             p = strchr(buf, '\r');
  443.             if (p)
  444.                 *p = 0;
  445.         
  446.             /*
  447.              *  Find base name of shell
  448.              */
  449.             shell_name = strrchr( buf, '/');
  450.             if (shell_name) {
  451.                 shell_name++;
  452.             } else {
  453.                 shell_name = &buf[2];/* skipping "#!" */
  454.             }
  455.  
  456.         } 
  457.         CloseHandle(exec_handle);
  458.     }
  459.  
  460.     flags = 0;
  461.  
  462.     if (file_not_found)
  463.         command_line = make_command_line( shell_name, exec_path, argv);
  464.     else
  465.         command_line = make_command_line( shell_name, file_info.szPathName,
  466.                  argv);
  467.  
  468.     if ( command_line == NULL ) {
  469.         pproc->last_err = 0;
  470.         pproc->lerrno = E_NO_MEM;
  471.         return(-1);
  472.     }
  473.  
  474.     if (envp) {
  475.         if (arr2envblk(envp, &envblk) ==FALSE) {
  476.             pproc->last_err = 0;
  477.             pproc->lerrno = E_NO_MEM;
  478.             free( command_line );
  479.             return(-1);
  480.         }
  481.     }
  482.  
  483.     if ((shell_name) || (file_not_found)) {
  484.         exec_path = 0;    /* Search for the program in %Path% */
  485.     } else {
  486.         exec_path = file_info.szPathName;
  487.     }
  488.  
  489.     /*
  490.      *  Set up inherited stdin, stdout, stderr for child
  491.      */
  492.     GetStartupInfo(&startInfo);
  493.     startInfo.dwFlags = STARTF_USESTDHANDLES;
  494.     startInfo.lpReserved = 0;
  495.     startInfo.cbReserved2 = 0;
  496.     startInfo.lpReserved2 = 0;
  497.     startInfo.lpTitle = shell_name ? shell_name : exec_path;
  498.     startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1];
  499.     startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1];
  500.     startInfo.hStdError = (HANDLE)pproc->sv_stderr[1];
  501.  
  502.     if (as_user) {
  503.         if (envblk) free(envblk);
  504.         return -1;
  505.     } else {
  506.         if (CreateProcess(
  507.             exec_path,
  508.             command_line,
  509.             NULL,
  510.             0, /* default security attributes for thread */
  511.             TRUE, /* inherit handles (e.g. helper pipes, oserv socket) */
  512.             flags, 
  513.             envblk,
  514.             0, /* default starting directory */
  515.             &startInfo,
  516.             &procInfo) == FALSE) {
  517.         
  518.             pproc->last_err = GetLastError();
  519.             pproc->lerrno = E_FORK;
  520.             fprintf(stderr, "process_begin: CreateProcess(%s, %s, ...) failed.\n", exec_path, command_line);
  521.             if (envblk) free(envblk);
  522.             free( command_line );
  523.             return(-1);
  524.         }
  525.     }
  526.     
  527.     pproc->pid = (int)procInfo.hProcess;
  528.     /* Close the thread handle -- we'll just watch the process */
  529.     CloseHandle(procInfo.hThread);
  530.     
  531.     /* Close the halves of the pipes we don't need */
  532.     if (pproc->sv_stdin) {
  533.         CloseHandle((HANDLE)pproc->sv_stdin[1]);
  534.         (HANDLE)pproc->sv_stdin[1] = 0;
  535.     }
  536.     if (pproc->sv_stdout) {
  537.         CloseHandle((HANDLE)pproc->sv_stdout[1]);
  538.         (HANDLE)pproc->sv_stdout[1] = 0;
  539.     }
  540.     if (pproc->sv_stderr) {
  541.         CloseHandle((HANDLE)pproc->sv_stderr[1]);
  542.         (HANDLE)pproc->sv_stderr[1] = 0;
  543.     }
  544.  
  545.     free( command_line );
  546.     if (envblk) free(envblk);
  547.     pproc->lerrno=0;
  548.     return 0;
  549. }
  550.  
  551.  
  552.  
  553. static DWORD 
  554. proc_stdin_thread(sub_process *pproc)
  555. {
  556.     DWORD in_done;
  557.     for (;;) {
  558.         if (WriteFile( (HANDLE) pproc->sv_stdin[0], pproc->inp, pproc->incnt,
  559.                      &in_done, NULL) == FALSE)
  560.             _endthreadex(0);
  561.         // This if should never be true for anonymous pipes, but gives
  562.         // us a chance to change I/O mechanisms later
  563.         if (in_done < pproc->incnt) {
  564.             pproc->incnt -= in_done;
  565.             pproc->inp += in_done;
  566.         } else {
  567.             _endthreadex(0);
  568.         }
  569.     }
  570.     return 0; // for compiler warnings only.. not reached
  571. }
  572.  
  573. static DWORD
  574. proc_stdout_thread(sub_process *pproc)
  575. {
  576.     DWORD bufsize = 1024;
  577.     char c;
  578.     DWORD nread;
  579.     pproc->outp = malloc(bufsize);
  580.     if (pproc->outp == NULL)
  581.         _endthreadex(0);
  582.     pproc->outcnt = 0;
  583.  
  584.     for (;;) {
  585.         if (ReadFile( (HANDLE)pproc->sv_stdout[0], &c, 1, &nread, NULL) 
  586.                     == FALSE) {
  587. /*            map_windows32_error_to_string(GetLastError());*/
  588.             _endthreadex(0);
  589.         }
  590.         if (nread == 0)
  591.             _endthreadex(0);
  592.         if (pproc->outcnt + nread > bufsize) {
  593.             bufsize += nread + 512; 
  594.             pproc->outp = realloc(pproc->outp, bufsize);
  595.             if (pproc->outp == NULL) {
  596.                 pproc->outcnt = 0;
  597.                 _endthreadex(0);
  598.             }
  599.         }
  600.         pproc->outp[pproc->outcnt++] = c;
  601.     }
  602.     return 0;
  603. }
  604.  
  605. static DWORD
  606. proc_stderr_thread(sub_process *pproc)
  607. {
  608.     DWORD bufsize = 1024;
  609.     char c;
  610.     DWORD nread;
  611.     pproc->errp = malloc(bufsize);
  612.     if (pproc->errp == NULL)
  613.         _endthreadex(0);
  614.     pproc->errcnt = 0;
  615.  
  616.     for (;;) {
  617.         if (ReadFile( (HANDLE)pproc->sv_stderr[0], &c, 1, &nread, NULL) == FALSE) {
  618.             map_windows32_error_to_string(GetLastError());
  619.             _endthreadex(0);
  620.         }
  621.         if (nread == 0)
  622.             _endthreadex(0);
  623.         if (pproc->errcnt + nread > bufsize) {
  624.             bufsize += nread + 512; 
  625.             pproc->errp = realloc(pproc->errp, bufsize);
  626.             if (pproc->errp == NULL) {
  627.                 pproc->errcnt = 0;
  628.                 _endthreadex(0);
  629.             }
  630.         }
  631.         pproc->errp[pproc->errcnt++] = c;
  632.     }
  633.     return 0;
  634. }
  635.  
  636.  
  637. /*
  638.  * Purpose: collects output from child process and returns results
  639.  *
  640.  * Description:
  641.  *
  642.  * Returns: 
  643.  *
  644.  * Notes/Dependencies:
  645.  */
  646.     long
  647. process_pipe_io(
  648.     HANDLE proc,
  649.     char *stdin_data, 
  650.     int stdin_data_len)
  651. {
  652.     sub_process *pproc = (sub_process *)proc;
  653.     bool_t stdin_eof = FALSE, stdout_eof = FALSE, stderr_eof = FALSE;
  654.     HANDLE childhand = (HANDLE) pproc->pid;
  655.     HANDLE tStdin, tStdout, tStderr;
  656.     DWORD dwStdin, dwStdout, dwStderr;
  657.     HANDLE wait_list[4];
  658.     DWORD wait_count;
  659.     DWORD wait_return;
  660.     HANDLE ready_hand;
  661.     bool_t child_dead = FALSE;
  662.  
  663.  
  664.     /*
  665.      *  Create stdin thread, if needed
  666.      */
  667.     pproc->inp = stdin_data;
  668.     pproc->incnt = stdin_data_len;
  669.     if (!pproc->inp) {
  670.         stdin_eof = TRUE;
  671.         CloseHandle((HANDLE)pproc->sv_stdin[0]);
  672.         (HANDLE)pproc->sv_stdin[0] = 0;
  673.     } else {
  674.         tStdin = (HANDLE) _beginthreadex( 0, 1024,
  675.             (unsigned (__stdcall *) (void *))proc_stdin_thread, pproc, 0,
  676.             (unsigned int *) &dwStdin);
  677.         if (tStdin == 0) {
  678.             pproc->last_err = GetLastError();
  679.             pproc->lerrno = E_SCALL;
  680.             goto done;
  681.         }
  682.     }
  683.     
  684.     /*
  685.      *   Assume child will produce stdout and stderr
  686.      */ 
  687.     tStdout = (HANDLE) _beginthreadex( 0, 1024,
  688.         (unsigned (__stdcall *) (void *))proc_stdout_thread, pproc, 0,
  689.         (unsigned int *) &dwStdout);
  690.     tStderr = (HANDLE) _beginthreadex( 0, 1024,
  691.         (unsigned (__stdcall *) (void *))proc_stderr_thread, pproc, 0,
  692.         (unsigned int *) &dwStderr);
  693.     
  694.     if (tStdout == 0 || tStderr == 0) {
  695.     
  696.         pproc->last_err = GetLastError();
  697.         pproc->lerrno = E_SCALL;
  698.         goto done;
  699.     }
  700.  
  701.  
  702.     /*
  703.      *  Wait for all I/O to finish and for the child process to exit
  704.      */
  705.  
  706.     while (!stdin_eof || !stdout_eof || !stderr_eof || !child_dead) {
  707.         wait_count = 0;
  708.         if (!stdin_eof) {
  709.             wait_list[wait_count++] = tStdin;
  710.         }
  711.         if (!stdout_eof) {
  712.             wait_list[wait_count++] = tStdout;
  713.         }
  714.         if (!stderr_eof) {
  715.             wait_list[wait_count++] = tStderr;
  716.         }
  717.         if (!child_dead) {
  718.             wait_list[wait_count++] = childhand;
  719.         }
  720.         
  721.         wait_return = WaitForMultipleObjects(wait_count, wait_list,
  722.              FALSE, /* don't wait for all: one ready will do */
  723.              child_dead? 1000 :INFINITE); /* after the child dies, subthreads have
  724.                  one second to collect all remaining output */
  725.         
  726.         if (wait_return == WAIT_FAILED) {
  727. /*            map_windows32_error_to_string(GetLastError());*/
  728.             pproc->last_err = GetLastError();
  729.             pproc->lerrno = E_SCALL;
  730.             goto done;
  731.         }
  732.  
  733.         ready_hand = wait_list[wait_return - WAIT_OBJECT_0];
  734.         
  735.         if (ready_hand == tStdin) {
  736.             CloseHandle((HANDLE)pproc->sv_stdin[0]);
  737.             (HANDLE)pproc->sv_stdin[0] = 0;
  738.             CloseHandle(tStdin);
  739.             tStdin = 0;
  740.             stdin_eof = TRUE;
  741.         
  742.         } else if (ready_hand == tStdout) {
  743.         
  744.               CloseHandle((HANDLE)pproc->sv_stdout[0]);
  745.             (HANDLE)pproc->sv_stdout[0] = 0;
  746.             CloseHandle(tStdout);
  747.             tStdout = 0;
  748.               stdout_eof = TRUE;
  749.         
  750.         } else if (ready_hand == tStderr) {
  751.             
  752.             CloseHandle((HANDLE)pproc->sv_stderr[0]);
  753.             (HANDLE)pproc->sv_stderr[0] = 0;
  754.             CloseHandle(tStderr);
  755.             tStderr = 0;
  756.             stderr_eof = TRUE;
  757.         
  758.         } else if (ready_hand == childhand) {
  759.             
  760.             if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
  761.                 pproc->last_err = GetLastError();
  762.                 pproc->lerrno = E_SCALL;
  763.                 goto done;
  764.             }
  765.             child_dead = TRUE;
  766.     
  767.         } else {
  768.         
  769.             /* ?? Got back a handle we didn't query ?? */
  770.             pproc->last_err = 0;
  771.             pproc->lerrno = E_FAIL;
  772.             goto done;
  773.         }
  774.     }
  775.  
  776.  done:
  777.     if (tStdin != 0)
  778.         CloseHandle(tStdin);
  779.     if (tStdout != 0)
  780.         CloseHandle(tStdout);
  781.     if (tStderr != 0)
  782.         CloseHandle(tStderr);
  783.  
  784.     if (pproc->lerrno)
  785.         return(-1);
  786.     else
  787.         return(0);
  788.  
  789. }
  790.  
  791. /*
  792.  * Purpose: collects output from child process and returns results
  793.  *
  794.  * Description:
  795.  *
  796.  * Returns: 
  797.  *
  798.  * Notes/Dependencies:
  799.  */
  800.     long
  801. process_file_io(
  802.     HANDLE proc)
  803. {
  804.     sub_process *pproc;
  805.     HANDLE childhand;
  806.     DWORD wait_return;
  807.  
  808.     if (proc == NULL)
  809.         pproc = process_wait_for_any_private();
  810.     else
  811.         pproc = (sub_process *)proc;
  812.  
  813.     /* some sort of internal error */
  814.     if (!pproc)
  815.         return -1;
  816.  
  817.     childhand = (HANDLE) pproc->pid;
  818.  
  819.     /*
  820.      * This function is poorly named, and could also be used just to wait
  821.      * for child death if you're doing your own pipe I/O.  If that is 
  822.      * the case, close the pipe handles here.
  823.      */
  824.     if (pproc->sv_stdin[0]) {
  825.         CloseHandle((HANDLE)pproc->sv_stdin[0]);
  826.         pproc->sv_stdin[0] = 0;
  827.     }
  828.     if (pproc->sv_stdout[0]) {
  829.         CloseHandle((HANDLE)pproc->sv_stdout[0]);
  830.         pproc->sv_stdout[0] = 0;
  831.     }
  832.     if (pproc->sv_stderr[0]) {
  833.         CloseHandle((HANDLE)pproc->sv_stderr[0]);
  834.         pproc->sv_stderr[0] = 0;
  835.     }
  836.  
  837.     /*
  838.      *  Wait for the child process to exit
  839.      */
  840.  
  841.     wait_return = WaitForSingleObject(childhand, INFINITE);
  842.         
  843.     if (wait_return != WAIT_OBJECT_0) {
  844. /*        map_windows32_error_to_string(GetLastError());*/
  845.         pproc->last_err = GetLastError();
  846.         pproc->lerrno = E_SCALL;
  847.         goto done2;
  848.     }
  849.  
  850.     if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
  851.         pproc->last_err = GetLastError();
  852.         pproc->lerrno = E_SCALL;
  853.     }
  854.     
  855. done2:
  856.     if (pproc->lerrno)
  857.         return(-1);
  858.     else
  859.         return(0);
  860.  
  861. }
  862.  
  863. /*
  864.  * Description:  Clean up any leftover handles, etc.  It is up to the
  865.  * caller to manage and free the input, ouput, and stderr buffers.
  866.  */
  867.     void
  868. process_cleanup(
  869.     HANDLE proc)
  870. {
  871.     sub_process *pproc = (sub_process *)proc;
  872.     int i;
  873.  
  874.     if (pproc->using_pipes) {
  875.         for (i= 0; i <= 1; i++) {
  876.             if ((HANDLE)pproc->sv_stdin[i])
  877.                 CloseHandle((HANDLE)pproc->sv_stdin[i]);
  878.             if ((HANDLE)pproc->sv_stdout[i])
  879.                 CloseHandle((HANDLE)pproc->sv_stdout[i]);
  880.             if ((HANDLE)pproc->sv_stderr[i])
  881.                 CloseHandle((HANDLE)pproc->sv_stderr[i]);
  882.         }
  883.     }
  884.     if ((HANDLE)pproc->pid)
  885.         CloseHandle((HANDLE)pproc->pid);
  886.     
  887.     free(pproc);
  888. }
  889.  
  890.  
  891. /*
  892.  * Try to protect against WINDOWS32 argument munging. This function takes
  893.  * an argv vector and outputs a 'protected' string as a return
  894.  * value. The return code can be safely passed to CreateProcess().
  895.  *
  896.  * The caller should free the return value.
  897.  */
  898.  
  899. #define TRACE(x)
  900. static char *fix_command_line(char *args[])
  901. {
  902.     int i;
  903.     char *narg;
  904.     char *nargp;
  905.     char *p;
  906.     char *q;
  907.     int alloc_len = 0;
  908.  
  909.     for (i = 0; args[i]; i++)
  910.         alloc_len += ((strlen(args[i]) * 2) + 1);
  911.     /* account for possible enclosing quotes and null termination */
  912.     alloc_len += 3;
  913.  
  914.     nargp = narg = malloc(alloc_len);
  915.  
  916.     for (i = 0; args[i]; i++) {
  917.         p = args[i];
  918.         TRACE(("original arg: %s\n", p));
  919.  
  920.         if (*p == '\0') {
  921.             *nargp++ = '"';
  922.             *nargp++ = '"';
  923.             *nargp = '\0';
  924.             TRACE(("empty string arg: %s\n", nargp-2));
  925.         } else if (strpbrk(p, "\" \t")) {
  926.             /* point to end of copy buffer */
  927.             q = narg;
  928.             q += (alloc_len-1);
  929.             *q-- = '\0'; /* ensure null terminated string */
  930.             *q-- = '"';  /* terminating quote of argument */
  931.  
  932.             /* point to end of the input string */
  933.             p = args[i];
  934.             p += strlen(args[i]);
  935.             p--;
  936.  
  937.             /* 
  938.              * Because arg is quoted, escape any backslashes 
  939.              * that might occur at the end of the string which
  940.              * proceed the closing quote.
  941.              * Example:
  942.              *     foo c:\
  943.              * Becomes:
  944.              *    "foo c:\\"
  945.              */
  946.             while (*p == '\\')
  947.                 *q-- = *p--, *q-- = '\\';
  948.  
  949.             /* copy the string in reverse */
  950.             while (p >= args[i]) {
  951.                 /* copy the character */
  952.                 *q-- = *p--;
  953.  
  954.                 /* 
  955.                  * Escape any double quote found. Also escape
  956.                  * each backslash preceding the double quote.
  957.                  */
  958.                 if (*(p+1) == '"') {
  959.                     *q-- = '\\';
  960.                     if (p >= args[i] && *p == '\\')
  961.                         while (p >= args[i] && *p == '\\')
  962.                             *q-- = *p--, *q-- = '\\';
  963.                 }
  964.             }
  965.  
  966.             /* finish quoting arg, q now points to complete arg */
  967.             *q = '"';
  968.  
  969.             /* rejustify */
  970.             memmove(nargp, q, strlen(q) + 1);
  971.             TRACE(("arg with white space or doublequotes: %s\n", nargp));
  972.             nargp += strlen(nargp);
  973.         } else {
  974.             /* just copy the argument, no protection needed */
  975.             strcpy(nargp, args[i]);
  976.             TRACE(("plain arg: %s\n", nargp));
  977.             nargp += strlen(nargp);
  978.         }
  979.  
  980.         /* separate arguments with spaces (if more args to gather) */
  981.         if (args[i+1])
  982.             *nargp++ = ' ';
  983.         *nargp   = '\0';
  984.     } /* end for */
  985.  
  986.     /* NULL terminate the arg list */
  987.     *nargp = '\0';
  988.  
  989.     return (narg);
  990. }
  991. #undef TRACE
  992.  
  993. /*
  994.  * Description: 
  995.  *     Create a command line buffer to pass to CreateProcess
  996.  *
  997.  * Returns:  the buffer or NULL for failure
  998.  *    Shell case:  sh_name a:/full/path/to/script argv[1] argv[2] ...
  999.  *  Otherwise:   argv[0] argv[1] argv[2] ...
  1000.  *
  1001.  * Notes/Dependencies: 
  1002.  *   CreateProcess does not take an argv, so this command creates a
  1003.  *   command line for the executable.  
  1004.  */
  1005.  
  1006. static char *
  1007. make_command_line( char *shell_name, char *exec_path, char **argv)
  1008. {
  1009.     char** nargv;
  1010.     char*  buf;
  1011.     int    i;
  1012.     char** shargv = NULL;
  1013.     char*  p = NULL;
  1014.     char*  q = NULL;
  1015.     int    j = 0;
  1016.  
  1017.     if (shell_name) {
  1018.         /* handle things like: #!/bin/sh -x */
  1019.  
  1020.         /* count tokens */
  1021.         q = strdup(shell_name);
  1022.         for (j = 0, p = q; (p = strtok(p, " \t")) != NULL; p = NULL, j++);
  1023.         free(q);
  1024.  
  1025.         /* copy tokens */
  1026.         q = strdup(shell_name);
  1027.         shargv = (char **) malloc((j+1) * sizeof (char *));
  1028.         for (j = 0, p = q; (p = strtok(p, " \t")) != NULL; p = NULL, j++)
  1029.             shargv[j] = strdup(p);
  1030.         shargv[j] = NULL;
  1031.         free(q);
  1032.  
  1033.         /* create argv */
  1034.         for (i = 0; argv[i]; i++);
  1035.         i += (j+1);
  1036.         nargv = (char **) malloc(i * sizeof (char *));
  1037.         for (i = 0; shargv[i] != NULL; i++)
  1038.             nargv[i] = shargv[i];
  1039.         for (j = 0; argv[j]; j++, i++)
  1040.             nargv[i] = argv[j];
  1041.         nargv[i] = NULL;
  1042.     } else
  1043.         nargv = argv;
  1044.  
  1045.     /* create string suitable for CreateProcess() */
  1046.     buf = fix_command_line(nargv);
  1047.  
  1048.     if (shell_name) {
  1049.         for (j = 0; shargv[j]; j++)
  1050.             free(shargv[j]);
  1051.         free(shargv);
  1052.         free(nargv);
  1053.     }
  1054.     
  1055.     return buf;
  1056. }
  1057.  
  1058. /*
  1059.  * Description: Given an argv and optional envp, launch the process
  1060.  *              using the default stdin, stdout, and stderr handles.
  1061.  *              Also, register process so that process_wait_for_any_private()
  1062.  *        can be used via process_file_io(NULL) or 
  1063.  *        process_wait_for_any().
  1064.  *
  1065.  * Returns: 
  1066.  *
  1067.  * Notes/Dependencies:  
  1068.  */
  1069. HANDLE
  1070. process_easy(
  1071.     char **argv,
  1072.     char **envp)
  1073. {
  1074.   HANDLE hIn;
  1075.   HANDLE hOut;
  1076.   HANDLE hErr;
  1077.   HANDLE hProcess;
  1078.  
  1079.   if (DuplicateHandle(GetCurrentProcess(),
  1080.                       GetStdHandle(STD_INPUT_HANDLE),
  1081.                       GetCurrentProcess(),
  1082.                       &hIn,
  1083.                       0,
  1084.                       TRUE,
  1085.                       DUPLICATE_SAME_ACCESS) == FALSE) {
  1086.     fprintf(stderr,
  1087.             "process_easy: DuplicateHandle(In) failed (e=%d)\n",
  1088.             GetLastError());
  1089.     return INVALID_HANDLE_VALUE;
  1090.   }
  1091.   if (DuplicateHandle(GetCurrentProcess(),
  1092.                       GetStdHandle(STD_OUTPUT_HANDLE),
  1093.                       GetCurrentProcess(),
  1094.                       &hOut,
  1095.                       0,
  1096.                       TRUE,
  1097.                       DUPLICATE_SAME_ACCESS) == FALSE) {
  1098.     fprintf(stderr,
  1099.            "process_easy: DuplicateHandle(Out) failed (e=%d)\n",
  1100.            GetLastError());
  1101.     return INVALID_HANDLE_VALUE;
  1102.   }
  1103.   if (DuplicateHandle(GetCurrentProcess(),
  1104.                       GetStdHandle(STD_ERROR_HANDLE),
  1105.                       GetCurrentProcess(),
  1106.                       &hErr,
  1107.                       0,
  1108.                       TRUE,
  1109.                       DUPLICATE_SAME_ACCESS) == FALSE) {
  1110.     fprintf(stderr,
  1111.             "process_easy: DuplicateHandle(Err) failed (e=%d)\n",
  1112.             GetLastError());
  1113.     return INVALID_HANDLE_VALUE;
  1114.   }
  1115.  
  1116.   hProcess = process_init_fd(hIn, hOut, hErr);
  1117.  
  1118.   if (process_begin(hProcess, argv, envp, argv[0], NULL)) {
  1119.     fake_exits_pending++;
  1120.     ((sub_process*) hProcess)->exit_code = process_last_err(hProcess);
  1121.  
  1122.     /* close up unused handles */
  1123.     CloseHandle(hIn);
  1124.     CloseHandle(hOut);
  1125.     CloseHandle(hErr);
  1126.   }
  1127.  
  1128.   process_register(hProcess);
  1129.  
  1130.   return hProcess;
  1131. }
  1132.